<template>
  <div class="ai-assistant-container">
    <!-- 侧边栏 -->
    <div class="sidebar">
      <div class="sidebar-header">
        <div class="logo">
          <img src="@/assets/logo1.png" alt="安恒信息">
          <span>AI攻防靶场</span>
        </div>
        <button class="new-chat-btn" @click="startNewChat">
          <i class="fas fa-plus"></i> 新对话
        </button>
      </div>

      <div class="chat-history">
        <div class="section-title">历史对话</div>
        <div
            v-for="chat in chatHistory"
            :key="chat.id"
            class="chat-item"
            :class="{ active: activeChatId === chat.id }"
            @click="selectChat(chat.id)"
        >
          <i class="fas fa-comment-alt"></i>
          <span class="truncate">{{ chat.title }}</span>
          <span class="time">{{ formatTime(chat.time) }}</span>
        </div>
      </div>

      <div class="sidebar-footer">
        <div class="user-profile">
          <img :src="userAvatar" alt="用户头像">
          <div>
            <div class="username">{{ userName }}</div>
            <div class="user-role">{{ userRole }}</div>
          </div>
          <i class="fas fa-cog"></i>
        </div>
      </div>
      <!-- 攻击和防御跳转按钮-->
      <div class="flaw-section">
        <div class="section-title">模拟攻防练习</div>
        <button class="flaw-btn" @click="startFlawDemo">
          <i class="fas fa-shield-alt"></i> 模拟环境练习
        </button>
      </div>

      <div class="attack-section">
        <div class="section-title">AI模拟攻击决策系统</div>
        <button class="attack-btn" @click="startAttackDemo">
          <i class="fas fa-fire"></i> AI攻击模拟
        </button>
      </div>

      <div class="defense-section">
        <div class="section-title">防御决策系统</div>
        <button class="defense-btn" @click="startDefenseDemo">
          <i class="fas fa-shield-alt"></i> 模拟攻击防御
        </button>
      </div>



    </div>

    <!-- 主聊天区域 -->
    <div class="main-chat-area">
      <div class="chat-header">
        <div class="chat-info">
          <h2>{{ activeChatTitle }}</h2>
          <div class="status-indicator">
            <span class="status-dot"></span>
            <span>AI助手在线</span>
          </div>
        </div>
        <div class="header-actions">
          <button class="action-btn" title="分享对话" @click="notImplemented">
            <i class="fas fa-share-alt"></i>
          </button>
          <button class="action-btn" title="导出对话" @click="exportChat">
            <i class="fas fa-download"></i>
          </button>
          <button class="action-btn" title="清空对话" @click="clearChat">
            <i class="fas fa-trash-alt"></i>
          </button>
        </div>
      </div>

      <div class="chat-messages" ref="messagesContainer">
        <div class="welcome-message" v-if="showWelcomePage">
          <div class="welcome-content">
            <h1>AI安全助手</h1>
            <p>我是您的AI安全助手，可以解答网络安全、攻防技术、漏洞分析等相关问题。</p>
            <p>请随时向我提问，我会尽力提供专业、准确的回答。</p>

            <div class="quick-questions">
              <div class="section-title">快速提问</div>
              <div class="question-grid">
                <div class="question-card" @click="quickQuestion('什么是SQL注入攻击？')">
                  <i class="fas fa-database"></i>
                  <span>什么是SQL注入攻击？</span>
                </div>
                <div class="question-card" @click="quickQuestion('如何防范XSS攻击？')">
                  <i class="fas fa-shield-alt"></i>
                  <span>如何防范XSS攻击？</span>
                </div>
                <div class="question-card" @click="quickQuestion('渗透测试的基本流程是什么？')">
                  <i class="fas fa-search"></i>
                  <span>渗透测试的基本流程</span>
                </div>
                <div class="question-card" @click="quickQuestion('解释一下零日漏洞的概念')">
                  <i class="fas fa-bug"></i>
                  <span>什么是零日漏洞？</span>
                </div>
              </div>
            </div>
          </div>
        </div>

        <template v-for="(message, index) in messages" :key="index">
          <div :class="['message', message.type === 'user' ? 'user-message' : 'ai-message']">
            <div class="avatar">
              <img v-if="message.type === 'user'" :src="userAvatar" alt="用户头像">
              <img v-else src="@/assets/icon/ai-avatar.png" alt="AI头像">
            </div>
            <div class="message-content">
              <div class="message-header">
                <span class="sender-name">{{ message.type === 'user' ? userName : 'AI安全助手' }}</span>
                <span class="message-time">{{ formatTime(message.timestamp) }}</span>
              </div>
              <div class="content">
                <template v-if="message.loading">
                  <div class="typing-indicator">
                    <span></span>
                    <span></span>
                    <span></span>
                  </div>
                </template>

                <template v-else>
                  <!-- 用户消息保持原样 -->
                  <template v-if="message.type === 'user'">
                    <p v-for="(paragraph, pIndex) in message.content.split('\n')" :key="pIndex">
                      {{ paragraph }}
                    </p>
                  </template>

                  <!-- AI 消息使用 Markdown 渲染 -->
                  <template v-else>
                    <div
                        class="markdown-content"
                        v-html="renderMarkdown(message.content)"
                    ></div>
                  </template>
                </template>

                <!-- 文件预览 - 支持多个文件 -->
                <div v-if="message.filesInfo && message.filesInfo.length > 0" class="file-previews">
                  <div v-for="(file, fileIndex) in message.filesInfo" :key="fileIndex" class="file-preview">
                    <div class="file-info">
                      <i class="fas fa-file"></i>
                      <span>{{ file.fileName }}</span>
                      <span class="file-size">({{ formatFileSize(file.size) }})</span>
                    </div>
                    <div class="file-actions">
                      <button class="action-btn small" @click="downloadFile(file.id, file.fileName)">
                        <i class="fas fa-download"></i> 下载
                      </button>
                      <button class="action-btn small danger" @click="deleteFile(file.id, index)">
                        <i class="fas fa-trash"></i> 删除
                      </button>
                    </div>
                  </div>
                </div>

                <!-- AI 消息的操作按钮 -->
                <div v-if="!message.loading && message.type === 'ai' && index === messages.length - 1" class="ai-actions">
                  <button class="action-btn small">
                    <i class="fas fa-thumbs-up"></i>
                  </button>
                  <button class="action-btn small">
                    <i class="fas fa-thumbs-down"></i>
                  </button>
                  <button class="action-btn small" @click="regenerateResponse">
                    <i class="fas fa-redo"></i>
                  </button>
                  <button class="action-btn small" @click="copyMessage(message.content)">
                    <i class="fas fa-copy"></i>
                  </button>
                </div>
              </div>
            </div>
          </div>
        </template>
      </div>

      <div class="chat-input-area">
        <!-- 显示已上传的附件 -->
        <div class="attached-files" v-if="attachedFiles.length > 0">
          <div class="section-title">附件</div>
          <div class="attached-files-list">
            <div
                v-for="(file, index) in attachedFiles"
                :key="file.id"
                class="attached-file-item"
                :class="{'has-error': file.status === 'error'}"
            >
              <div class="file-status">
                <!-- 上传中状态 -->
                <div v-if="file.status === 'uploading'" class="uploading-spinner"></div>

                <!-- 完成状态 -->
                <i v-else-if="file.status === 'completed'" class="fas fa-file"></i>

                <!-- 错误状态 -->
                <i v-else class="fas fa-exclamation-circle status-error"></i>
              </div>

              <span class="file-name">{{ file.fileName }}</span>
              <span class="file-size">({{ formatFileSize(file.size) }})</span>

              <!-- 错误提示 -->
              <div v-if="file.status === 'error'" class="file-error-tooltip">
                {{ file.error || '上传失败' }}
              </div>

              <button class="remove-btn" @click="removeAttachedFile(index)">
                <i class="fas fa-times"></i>
              </button>
            </div>
          </div>
        </div>

        <div class="input-actions">
          <input
              type="file"
              id="file-upload"
              ref="fileInput"
              style="display: none"
              @change="handleFileUpload"
              :disabled="fileUploading"
          >
          <button
              class="action-btn file-upload-btn"
              title="上传文件"
              @click="triggerFileUpload"
              :disabled="fileUploading"
          >
            <i class="fas fa-paperclip"></i>
          </button>
          <button class="action-btn" title="添加代码片段" @click="notImplemented">
            <i class="fas fa-code"></i>
          </button>
          <button class="action-btn" title="表情符号" @click="notImplemented">
            <i class="fas fa-smile"></i>
          </button>
        </div>

        <div class="input-container">
          <textarea
              v-model="userInput"
              placeholder="输入问题..."
              @keyup.enter.exact.prevent="sendMessage"
              @keydown.shift.enter="handleNewLine"
              ref="messageInput"
          ></textarea>
          <button
              class="send-btn"
              @click="sendMessage"
              :disabled="(!userInput.trim() && attachedFiles.length === 0) || loading"
          >
            <i v-if="loading" class="fas fa-spinner fa-spin"></i>
            <i v-else class="fas fa-paper-plane"></i>
          </button>
        </div>

        <div class="input-footer">
          <div class="ai-tips">
            <i class="fas fa-lightbulb"></i>
            <span>提示：您可以询问关于网络安全、渗透测试、漏洞分析等问题</span>
          </div>
          <div class="model-info">
            <span>模型: Deepseek</span>
            <span>安全级别: 高级</span>
          </div>
        </div>
      </div>
    </div>
    <div class="attack-overlay" :class="{ active: showAttackPanel }" @click.self="closeAttackPanel">
      <AttackPanel
          :isActive="showAttackPanel"
          @close="showAttackPanel = false"
          @defense-message="handleAttackMessage"
      />
    </div>
    <div class="defense-overlay" :class="{ active: showDefensePanel }" @click.self="closeDefensePanel">
      <DefensePanel
          :isActive="showDefensePanel"
          @close="showDefensePanel = false"
          @defense-message="handleDefenseMessage"
      />
    </div>

  </div>
</template>

<script>
import { marked } from 'marked';
import { useClipboard } from '@vueuse/core';
import DOMPurify from 'dompurify';
import DefensePanel from '@/components/DefensePanel.vue';
import AttackPanel from "@/components/AttackPanel.vue";

export default {
  name: 'AiAssistant',
  components: {
    AttackPanel,
    DefensePanel
  },
  data() {
    return {
      showAttackPanel:false,
      showDefensePanel: false,
      messages: [],
      // 存储待发送的附件
      attachedFiles: [],
      userInput: '',
      currentConversationId: '',
      loading: false,
      userName: '安全研究员',
      userRole: '高级安全分析师',
      userAvatar: require('@/assets/icon/user-avatar.png'),
      chatHistory: [
        {
          id: 1,
          title: '渗透测试相关问题',
          time: new Date(Date.now() - 3600000),
          messages: [
            { type: 'user', content: '渗透测试的基本流程是什么？', timestamp: new Date(Date.now() - 3600000) },
            { type: 'ai', content: '渗透测试通常包括以下阶段...', timestamp: new Date(Date.now() - 3600000 + 30000) }
          ]
        },
      ],
      fileUploading: false, // 添加文件上传状态标志
      activeChatId: null,
      showWelcomePage: true,
      isInitialLoad: true,
      apiToken: 'Bearer 8df24c809b764c3eb18979bf695932a9.8IIbCoHl5veiMkQP', // 替换为您的实际API token
      apiBaseUrl: 'https://open.bigmodel.cn/api/paas/v4'
    }
  },
  computed: {
    activeChatTitle() {
      if (!this.activeChatId) return '新对话';
      const chat = this.chatHistory.find(c => c.id === this.activeChatId);
      return chat ? chat.title : '新对话';
    }
  },
  mounted() {
    if (this.isInitialLoad) {
      this.isInitialLoad = false;
    }
  },
  methods: {
    startFlawDemo(){
      this.$router.push('/flaw_main');
    },
    //攻击部分
    startAttackDemo() {
      // 添加启动消息
      this.addMessage('ai', '🚀 启动AI自主模拟攻击系统...');
      // 短暂延迟后展示攻击面板
      setTimeout(() => {
        this.showAttackPanel = true;
        // 添加攻击消息
        setTimeout(() => {
          this.addMessage('ai', '🔍 检测到多种攻击尝试...');
        }, 500);
      }, 1000);
    },
    //关闭攻击
    closeAttackPanel() {
      this.showAttackPanel = false;
      // 添加完成消息
      this.addMessage('ai', '🛡️ 模拟流程完成! 已生成可行攻击策略');
    },
    startDefenseDemo() {
      // 添加启动消息
      this.addMessage('ai', '🚀 启动自适应防御系统...');

      // 短暂延迟后展示防御面板
      setTimeout(() => {
        this.showDefensePanel = true;

        // 添加攻击检测消息
        setTimeout(() => {
          this.addMessage('ai', '🔍 检测到SQL注入攻击尝试...');
        }, 500);
      }, 1000);
    },
    closeDefensePanel() {
      this.showDefensePanel = false;
      // 添加完成消息
      this.addMessage('ai', '🛡️ 防御流程完成! 所有威胁已成功处置');
    },

    triggerFileUpload() {
      this.$refs.fileInput.click();
    },

    // 处理文件上传
    async handleFileUpload(e) {
      const file = e.target.files[0];
      if (!file) return;
      e.target.value = '';

      // 添加到附件列表并标记为上传中
      this.attachedFiles.push({
        id: 'uploading-' + Date.now(), // 临时ID
        fileName: file.name,
        size: file.size,
        status: 'uploading' // 添加状态字段
      });

      this.fileUploading = true;

      try {
        // 上传文件并获取内容
        const fileId = await this.uploadFile(file);
        const fileContent = await this.getFileContent(fileId);

        // 更新附件状态
        const fileIndex = this.attachedFiles.findIndex(f => f.fileName === file.name);
        if (fileIndex !== -1) {
          this.attachedFiles[fileIndex] = {
            id: fileId,
            fileName: file.name,
            size: file.size,
            content: fileContent,
            status: 'completed' // 更新为完成状态
          };
        }
      } catch (error) {
        console.error('文件处理失败:', error);

        // 更新为错误状态
        const fileIndex = this.attachedFiles.findIndex(f => f.fileName === file.name);
        if (fileIndex !== -1) {
          this.attachedFiles[fileIndex].status = 'error';
          this.attachedFiles[fileIndex].error = error.message || '上传失败';
        }
      } finally {
        this.fileUploading = false;
      }
    },

    // 移除附件
    removeAttachedFile(index) {
      if (this.attachedFiles[index].status === 'uploading') {
        if (!confirm('文件正在上传中，确定要取消吗？')) return;
      }
      this.attachedFiles.splice(index, 1);
    },

    // 上传文件
    async uploadFile(file) {
      const url = `${this.apiBaseUrl}/files`;
      const form = new FormData();
      form.append('purpose', 'file-extract');
      form.append('file', file);

      const options = {
        method: 'POST',
        headers: {
          'Authorization': this.apiToken
        },
        body: form
      };

      try {
        const response = await fetch(url, options);
        if (!response.ok) {
          throw new Error(`上传失败: ${response.status} ${response.statusText}`);
        }

        const data = await response.json();
        return data.id;

      } catch (error) {
        throw new Error(`上传文件时出错: ${error.message}`);
      }
    },

    // 获取文件内容
    async getFileContent(fileId) {
      const url = `${this.apiBaseUrl}/files/${fileId}/content`;
      const options = {
        method: 'GET',
        headers: {
          'Authorization': this.apiToken
        }
      };

      try {
        const response = await fetch(url, options);
        if (!response.ok) {
          throw new Error(`获取内容失败: ${response.status} ${response.statusText}`);
        }

        return await response.text();

      } catch (error) {
        throw new Error(`获取文件内容时出错: ${error.message}`);
      }
    },

    // 删除文件
    async deleteFile(fileId, messageIndex) {
      if (!confirm('确定要删除此文件吗？')) return;

      const url = `${this.apiBaseUrl}/files/${fileId}`;
      const options = {
        method: 'DELETE',
        headers: {
          'Authorization': this.apiToken
        }
      };

      try {
        const response = await fetch(url, options);
        if (!response.ok) {
          throw new Error(`删除失败: ${response.status} ${response.statusText}`);
        }

        // 从消息中移除文件信息
        if (this.messages[messageIndex]) {
          this.messages[messageIndex].filesInfo = this.messages[messageIndex].filesInfo?.filter(
              file => file.id !== fileId
          ) || null;
        }

      } catch (error) {
        console.error('删除文件失败:', error);
        alert(`删除文件失败: ${error.message || '未知错误'}`);
      }
    },

    // 下载文件
    async downloadFile(fileId, fileName) {
      try {
        // 获取文件内容
        const content = await this.getFileContent(fileId);

        // 创建Blob对象
        const blob = new Blob([content], {type: 'text/plain'});
        const url = URL.createObjectURL(blob);

        // 创建下载链接
        const a = document.createElement('a');
        a.href = url;
        a.download = fileName || 'downloaded-file.txt';
        document.body.appendChild(a);
        a.click();

        // 清理
        setTimeout(() => {
          document.body.removeChild(a);
          URL.revokeObjectURL(url);
        }, 100);

      } catch (error) {
        console.error('下载文件失败:', error);
        alert(`下载文件失败: ${error.message || '未知错误'}`);
      }
    },

    // 格式化文件大小
    formatFileSize(bytes) {
      if (bytes === 0) return '0 Bytes';
      const k = 1024;
      const sizes = ['Bytes', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },

    renderMarkdown(content) {
      // 配置 marked 选项
      marked.setOptions({
        breaks: true, // 启用 GFM 换行符
        gfm: true,    // 启用 GitHub Flavored Markdown
      });

      return DOMPurify.sanitize(marked.parse(content));
    },

    notImplemented() {
      alert('该功能正在开发中，敬请期待！');
    },

    exportChat() {
      const content = this.messages.map(msg => {
        return `${msg.type === 'user' ? '用户' : 'AI'} (${this.formatTime(msg.timestamp)}):\n${msg.content}`;
      }).join('\n\n');

      const blob = new Blob([content], {type: 'text/plain'});
      const link = document.createElement('a');
      link.href = URL.createObjectURL(blob);
      link.download = `AI对话_${new Date().toISOString().slice(0, 10)}.txt`;
      link.click();
    },

    clearChat() {
      if (confirm('确定要清空当前对话吗？')) {
        this.messages = [];
        this.attachedFiles = []; // 清空附件列表
        this.showWelcomePage = true;
      }
    },

    copyMessage(content) {
      const {copy} = useClipboard();
      copy(content);
      alert('内容已复制到剪贴板');
    },

    async sendMessage() {
      if ((!this.userInput.trim() && this.attachedFiles.length === 0) || this.loading) return;

      if (this.showWelcomePage) {
        this.showWelcomePage = false;
      }

      // 添加用户消息（包含文本和附件）
      let userContent = this.userInput;
      const attachedFilesInfo = [...this.attachedFiles];

      if (this.attachedFiles.length > 0) {
        userContent += `\n\n[附件]`;
        attachedFilesInfo.forEach(file => {
          userContent += `\n- ${file.fileName}`;
        });
      }

      this.addMessage('user', userContent, false, {
        files: attachedFilesInfo
      });

      // 准备发送给AI的内容（包含文本和文件内容）
      let aiInput = this.userInput;
      if (this.attachedFiles.length > 0) {
        aiInput += "\n\n附加文件内容:";
        this.attachedFiles.forEach(file => {
          // 只发送文件的前1000个字符避免过长
          const truncatedContent = file.content.length > 1000
              ? file.content.substring(0, 1000) + '...'
              : file.content;
          aiInput += `\n\n[文件: ${file.fileName}]\n${truncatedContent}`;
        });
      }

      // 清空输入和附件
      this.userInput = '';
      this.attachedFiles = [];

      try {
        this.loading = true;
        this.addMessage('ai', '', true);

        const response = await fetch('http://localhost:8000/api/ai/chat/', {
          method: 'POST',
          headers: {'Content-Type': 'application/json'},
          body: JSON.stringify({
            message: aiInput,
            conversation_id: this.currentConversationId
          }),
        });

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json();

        if (this.messages.length > 0 && this.messages[this.messages.length - 1].loading) {
          this.messages.pop();
        }

        if (data.success) {
          this.addMessage('ai', data.reply);
          // 保存conversation_id
          if (data.conversation_id) {
            this.currentConversationId = data.conversation_id;
          }
        } else {
          this.addMessage('ai', '抱歉，发生了错误，请重试。');
        }
      } catch (error) {
        console.error('Error:', error);
        if (this.messages.length > 0 && this.messages[this.messages.length - 1].loading) {
          this.messages.pop();
        }
        this.addMessage('ai', '连接出错');
      } finally {
        this.loading = false;
      }

      if (this.activeChatId) {
        const index = this.chatHistory.findIndex(c => c.id === this.activeChatId);
        if (index !== -1) {
          this.chatHistory[index].messages = [...this.messages];
          this.chatHistory[index].time = new Date();
          this.chatHistory[index].conversationId = this.currentConversationId; // 添加这一行
        }
      } else {
        const newChat = {
          id: Date.now(),
          title: userContent.substring(0, 20) + (userContent.length > 20 ? '...' : ''),
          time: new Date(),
          messages: [...this.messages],
          conversationId: this.currentConversationId // 添加这一行
        };
        this.chatHistory.unshift(newChat);
        this.activeChatId = newChat.id;
      }
    },

    addMessage(type, content, loading = false, fileInfo = null) {
      const message = {
        type,
        content,
        loading,
        timestamp: new Date()
      };

      if (fileInfo && fileInfo.files) {
        message.filesInfo = fileInfo.files.map(file => ({
          id: file.id,
          fileName: file.fileName,
          size: file.size
        }));
      }

      this.messages.push(message);
      this.scrollToBottom();
    },

    scrollToBottom() {
      const container = this.$refs.messagesContainer;
      if (container) {
        container.scrollTop = container.scrollHeight;
      }
    },

    formatTime(date) {
      return date.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
    },

    handleNewLine() {
      this.userInput += '\n';
    },

    startNewChat() {
      this.messages = [];
      this.attachedFiles = []; // 清空附件列表
      this.activeChatId = null;
      this.userInput = '';
      this.showWelcomePage = true;
      this.currentConversationId = ''; // 清空Conversation ID
    },

    selectChat(chatId) {
      const chat = this.chatHistory.find(c => c.id === chatId);
      if (chat) {
        this.messages = [...chat.messages];
        this.activeChatId = chatId;
        this.showWelcomePage = false;
        this.currentConversationId = chat.conversationId || '';
      }
    },

    quickQuestion(question) {
      if (this.showWelcomePage) {
        this.showWelcomePage = false;
      }

      this.userInput = question;
      this.$nextTick(() => {
        this.sendMessage();
      });
    },

    regenerateResponse() {
      if (this.messages.length < 2) return;

      const lastUserMessage = this.messages[this.messages.length - 2];
      if (lastUserMessage.type === 'user') {
        this.messages.pop();
        this.userInput = lastUserMessage.content;
        this.sendMessage();
      }
    }
  }
}
</script>

<style scoped>

@import "@/assets/styles/ai-assistant.css";

/* Markdown 渲染样式 */
.markdown-content {
  line-height: 1.6;
}

.markdown-content h1,
.markdown-content h2,
.markdown-content h3 {
  margin-top: 1em;
  margin-bottom: 0.5em;
}

.markdown-content h1 {
  font-size: 1.5em;
  border-bottom: 1px solid #eee;
  padding-bottom: 0.3em;
}

.markdown-content h2 {
  font-size: 1.3em;
}

.markdown-content h3 {
  font-size: 1.1em;
}

.markdown-content code {
  background-color: #f5f5f5;
  padding: 0.2em 0.4em;
  border-radius: 3px;
  font-family: monospace;
  color: #e83e8c;
}

.markdown-content pre {
  background-color: #2d2d2d;
  color: #f8f8f2;
  padding: 1em;
  border-radius: 5px;
  overflow-x: auto;
  margin: 1em 0;
}

.markdown-content pre code {
  background: none;
  color: inherit;
  padding: 0;
}

.markdown-content ul,
.markdown-content ol {
  padding-left: 1.5em;
  margin-bottom: 1em;
}

.markdown-content li {
  margin-bottom: 0.5em;
}

.markdown-content blockquote {
  border-left: 4px solid #ddd;
  padding-left: 1em;
  color: #666;
  margin: 1em 0;
}

/* 打字指示器 */
.typing-indicator {
  display: flex;
  padding: 10px 0;
}

.typing-indicator span {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: #888;
  margin: 0 2px;
  animation: typing 1s infinite;
}

.typing-indicator span:nth-child(2) {
  animation-delay: 0.2s;
}

.typing-indicator span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes typing {
  0%, 60%, 100% {
    transform: translateY(0);
  }
  30% {
    transform: translateY(-5px);
  }
}

/* 文件预览样式 */
.file-previews {
  margin-top: 15px;
}

.file-preview {
  margin-bottom: 10px;
  padding: 10px;
  background-color: #f8f9fa;
  border-radius: 8px;
  border: 1px solid #e9ecef;
}

.file-info {
  display: flex;
  align-items: center;
  margin-bottom: 10px;
}

.file-info i {
  margin-right: 10px;
  font-size: 1.2em;
  color: #495057;
}

.file-info span {
  font-size: 0.95em;
  color: #495057;
}

.file-size {
  margin-left: 8px;
  color: #6c757d !important;
  font-size: 0.85em !important;
}

.file-actions {
  display: flex;
  gap: 8px;
}

.action-btn.small {
  padding: 4px 8px;
  font-size: 0.8em;
}

.action-btn.danger {
  background-color: #f8d7da;
  color: #721c24;
  border-color: #f5c6cb;
}

.action-btn.danger:hover {
  background-color: #f1b0b7;
}

/* 附件显示区域样式 */
.attached-files {
  padding: 10px 15px;
  background-color: #f8f9fa;
  border-bottom: 1px solid #e9ecef;
}

.section-title {
  font-size: 0.85em;
  color: #6c757d;
  margin-bottom: 5px;
}

.attached-files-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.attached-file-item {
  display: flex;
  align-items: center;
  background-color: #e9ecef;
  border-radius: 4px;
  padding: 5px 10px;
  font-size: 0.9em;
}

.file-name {
  margin: 0 8px;
  max-width: 150px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.file-size {
  color: #6c757d;
  font-size: 0.8em;
}

.remove-btn {
  background: none;
  border: none;
  color: #6c757d;
  cursor: pointer;
  margin-left: 5px;
  padding: 2px;
}

.remove-btn:hover {
  color: #dc3545;
}

/* 添加附件加载状态样式 */
.attached-file-item {
  position: relative;
  display: flex;
  align-items: center;
  background-color: #e9ecef;
  border-radius: 4px;
  padding: 5px 10px;
  font-size: 0.9em;
  transition: all 0.3s ease;
}

.file-status {
  margin-right: 8px;
  display: flex;
  align-items: center;
}

.uploading-spinner {
  width: 16px;
  height: 16px;
  border: 2px solid rgba(59, 130, 246, 0.2);
  border-top: 2px solid #3b82f6;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.status-error {
  color: #ef4444;
}

.file-error-tooltip {
  position: absolute;
  bottom: 100%;
  left: 0;
  background: #fef2f2;
  color: #b91c1c;
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 0.8em;
  white-space: nowrap;
  z-index: 10;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  margin-bottom: 5px;
  display: none;
}

.has-error:hover .file-error-tooltip {
  display: block;
}

/* 调整文件上传按钮状态 */
.file-upload-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
</style>
